package com.taldh.algorithm.lock;

import java.util.concurrent.atomic.AtomicReference;

/**
 * MCS：John Mellor-Crummey and Michael Scott
 * CLH：Craig，Landin and Hagersten
 * @author zhibo
 * @version 1.0
 * @date 2018/11/7 10:39
 */
public class CLHLock implements Lock{
    private AtomicReference<CLHNode> tail;
    private ThreadLocal<CLHNode> threadLocal;

    public CLHLock() {
        this.tail = new AtomicReference<>();
        this.threadLocal = new ThreadLocal<>();
    }

    public void lock() {
        CLHNode curNode = threadLocal.get();
        if(curNode == null){
            curNode = new CLHNode();
            threadLocal.set(curNode);
        }

        //通过getAndSet方法实现了隐式链表，（各个节点之间好像有一个前驱指针指向前一个节点，其实没有）,
        //它是通过getAndSet这个方法的返回值去获取当前CLHLock的tail以前指向的节点，也就是当前尾节点的前一个节点。
        //比如现在尾节点是A，现在调用方法getAndSet（B）;现在tail指向B，方法返回A，如果A不为空的话就去空转，
        // 直到A节点的状态变更（变更在unlock中）。
        //这就是隐式链表形成的原因
        //所以这个算法的锁是在它的前一个节点空转。
        CLHNode predNode = tail.getAndSet(curNode);
        if(predNode != null){
            while (predNode.getLocked()){
                System.out.println("Thread is lock looping = " + Thread.currentThread().getName());
            }
        }
    }
    // 这里有两个问题：1。tail是如何接上的。   2。按照运行的内容看，每个线程都在轮询，这个是否太浪费cpu了？

    public void unlock() {
        CLHNode curNode = threadLocal.get();
        threadLocal.remove();

        if(curNode == null || curNode.getLocked() == false){
            return;
        }

        if(!tail.compareAndSet(curNode, null)){
            curNode.setLocked(false);
        }
    }

    public static void main(String[] args) {
        final Lock clhLock = new CLHLock();

        for (int i = 0; i < 2; i++) {
            Thread thread = new Thread(new DemoTask(clhLock, i + ""));
            thread.setName("Thread "+i);
            thread.start();
        }
    }

    class CLHNode {
        private volatile boolean locked = true;

        public boolean getLocked() {
            return locked;
        }

        public void setLocked(boolean locked) {
            this.locked = locked;
        }
    }
}

